#include "world/noise.hpp"

#include <ctime>
#include <cstdlib>

using namespace std;

// Random Percision
#define N 999

inline
float Interpolate(float x0, float x1, float alpha)
{
	return x0 + alpha * (x1 - x0);
}


vector<vector<float>> dw::GenerateWhiteNoise(uint32_t row, uint32_t col, uint32_t seed)
{
	srand(seed);
	vector<vector<float>> noise(row, vector<float>(col, 0));
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			noise[i][j] = rand() % (N + 1) / float(N + 1);
		}
	}
	return noise;
}


vector<vector<float>> dw::GenerateSmoothNoise(const vector<vector<float>>& baseNoise, int octave)
{
	int row = baseNoise.size();
	int col = baseNoise[0].size();

	vector<vector<float>> smoothNoise(row, vector<float>(col, 0));
	int samplePeriod = 1 << octave;
	float sampleFrequency = 1.0f / samplePeriod;

	for (int i = 0; i < row; i++)
	{
		int sample_i0 = (i / samplePeriod) * samplePeriod;
		int sample_i1 = (sample_i0 + samplePeriod) % row;
		float horizontal_blend = (i - sample_i0) * sampleFrequency;

		for (int j = 0; j < col; j++)
		{
			// Calculate vertical sampling rate
			int sample_j0 = (j / samplePeriod) * samplePeriod;
			int sample_j1 = (sample_j0 + samplePeriod) % col;
			float vertical_blend = (j - sample_j0) * sampleFrequency;

			// Mix the top two corners
			float top = Interpolate(baseNoise[sample_i0][sample_j0],
				                    baseNoise[sample_i1][sample_j0], horizontal_blend);

			// Mix the bottom two corners
			float bottom = Interpolate(baseNoise[sample_i0][sample_j1],
				                       baseNoise[sample_i1][sample_j1], horizontal_blend);

			// Mix result
			smoothNoise[i][j] = Interpolate(top, bottom, vertical_blend);

		}
	}
	return smoothNoise;
}


vector<vector<float>> dw::GeneratePerlinNoise(const vector<vector<float>>& baseNoise, int octaveCount)
{
	int row = baseNoise.size();
	int col = baseNoise[0].size();

	vector<vector<vector<float>>> smoothNoise(octaveCount);
	float persistance = 0.5f;

	for (int i = 0; i < octaveCount; i++)
	{
		smoothNoise[i] = dw::GenerateSmoothNoise(baseNoise, i);
	}

	vector<vector<float>> perlinNoise(row, vector<float>(col));
	float amplitude = 1.0f;
	float totalAmplitude = 0.0f;

	// Mix Noises
	for (int octave = octaveCount - 1; octave >= 0; octave--)
	{
		amplitude *= persistance;
		totalAmplitude += amplitude;

		for (int i = 0; i < row; i++)
		{
			for (int j = 0; j < col; j++)
			{
				perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
			}
		}
	}

	// Normalization
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			perlinNoise[i][j] /= totalAmplitude;
		}
	}

	return perlinNoise;
}